Python fastapi
Table of Contents
介绍
FastAPI 是一个现代、快速、开源的 Web 框架,用于构建高性能的 RESTful API。
安装
$ python -m pip install "fastapi[all]"
tree .
├── config
├── lib # 存放自定义模块(无变动或变动少)
│ └── common.py
├── LICENSE
├── log # 存放日志文件
├── temp # 存放临时文件
├── static # 静态文件
├── .gitignore # git 忽略文件
├── main.py # 启动文件
├── README.md
├── requirement.txt # 项目依赖包
├── api # api 路由
│ └── v1
│ └── node.py
└── utils # 自定义模块
├── database.py # 数据库连接
├── logger.py # logger 初始化
├── middleware.py # 中间件
├── router.py # 路由配置
└── schemas.py # fastapi 参数模型
fastapi 入口
from contextlib import asynccontextmanager
import uvicorn
from loguru import logger
from fastapi import FastAPI
from utils.logger import logger_setting
from utils.router import add_routers
from utils.database import Database
from utils.middleware import add_middlewares
HOST = "localhost"
PORT = 8010
@asynccontextmanager
async def lifespan(router: FastAPI):
""" 应用开启和结束操作 """
logger_setting()
Database.init()
add_routers(router)
logger.info("app start")
yield
logger.info("app close")
Database.close()
app = FastAPI(
title="FastAPI",
description=f"{HOST}:{PORT} api",
version="0.0.1",
lifespan=lifespan
)
add_middlewares(app)
if __name__ == "__main__":
uvicorn.run("main:app", host=HOST, port=PORT, reload=True)
浏览器打开 http://0.0.0.0:8010/
swagger-ui http://0.0.0.0:8010/docs/
swagger-ui
打开 swagger 页面需要加载外界 CDN 资源,可能会非常慢,建议使用离线 swagger-ui 文件
在 Github 下载官方 swagger-ui 资源, 放入项目 static
路径下, 在代码中配置即可
project
├─ static
│ └── swagger-ui
│ ├── ...
│ └── README.md
└── main.py
在 utils/router.py
中挂载 swagger 并配置可用路由
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi.openapi.docs import get_swagger_ui_html
from api.v1 import student, article
from lib.common import abs_dir
def add_routers(app: FastAPI):
""" app add router"""
# 添加路由
app.include_router(student.router, prefix="/api/v1", tags=["student"])
app.include_router(article.router, prefix="/api/v1", tags=["article"])
# mount swagger 静态文件
app.mount('/static', StaticFiles(
directory=abs_dir("static", "swagger-ui", "dist")),
name="static"
)
# 定义 swagger 挂载路由
@app.get("/", include_in_schema=False)
async def custom_swagger_ui_html():
""" set local static swagger """
return get_swagger_ui_html(openapi_url=app.openapi_url, title=app.title)
database
定义数据连接和使用
from contextlib import contextmanager
from dataclasses import dataclass
import psycopg
from psycopg_pool import ConnectionPool
from psycopg.rows import dict_row
from loguru import logger
@dataclass
class DBConnect:
""" 数据库连接 """
host: str
port: int
dbname: str
user: str
password: str
DEFAULT_DATABASE: DBConnect = DBConnect(
host="localhost",
port="5432",
dbname="learn",
user="postgres",
password="password"
)
class Database:
""" database module """
pool: ConnectionPool| None = None
db: DBConnect = DEFAULT_DATABASE
@classmethod
def init(cls, db: DBConnect|None=None) -> None:
""" 数据库初始化连接 """
cls.db = db if db else cls.db
cls.pool: ConnectionPool = ConnectionPool(
min_size=1,
max_size=10,
conninfo=" ".join(f"{k}={v}" for k, v in vars(cls.db).items())
)
@contextmanager
def __new__(cls, db: DBConnect|None=None):
_ = cls.init(db) if db else None
try:
conn = cls.pool.getconn()
cursor = conn.cursor(row_factory=dict_row)
yield cursor
conn.commit()
except Exception as e:
_ = conn.rollback() if conn else None
logger.error(f"error: {e}")
finally:
_ = cursor.close() if cursor else None
_ = cls.pool.putconn(conn) if cls.pool else None
if __name__ == "__main__":
# 起服务时连接数据库
Database.init()
# 使用连接池执行 sql 语句
with Database() as cursor:
cursor.execute("SELECT id, title FROM article")
print(cursor.fetchall())
api 路由
定义 fastapi api 路由
from fastapi import APIRouter, Depends
from loguru import logger
from psycopg import Cursor
from utils.database import Database
from utils.schemas import Student
router = APIRouter()
created = Database.create_session
@router.get("/articles")
def get_articles(session: Cursor = Depends(created)):
return session.execute("SELECT * FROM article").fetchall()
@router.get("/articles/{id}")
def get_article(id: int, session: Cursor = Depends(created)):
return session.execute("SELECT * FROM article WHERE id = %s", (id,)).fetchone()
日志
设置日志打印格式, 输出到指定文件
from loguru import logger
from lib.common import abs_dir
def logger_setting():
""" 设置 logger """
logger.remove()
fmt = '[<level>{level: <8}</level>][<green>{time:YYYY-MM-DD HH:mm:ss}</green>]: <level>{message}</level>'
logger.add(sys.stderr, level='INFO', format=fmt)
logger.add(abs_dir('log', 'report.log'),
level='INFO', format=fmt, rotation='1 week', retention='30 days'
)
中间件
自定义请求开始和请求结束后的操作
from traceback import format_exc
from fastapi.middleware.cors import CORSMiddleware
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from loguru import logger
def add_middlewares(app: FastAPI):
""" 允许所有请求 """
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.middleware("http")
async def catch_exceptions(request: Request, call_next):
""" 捕获所有接口执行异常 """
try:
response = await call_next(request)
return response
except Exception as e:
logger.error(format_exc())
return JSONResponse(status_code=500, content={"message": "Server run error"})